#include <stdlib.h>
#include <glib.h>
#include <SDL.h>
#include "racanhack.h"
#include "video.h"
#include "task.h"
#include "cave.h"
#include "player.h"

static GList *sprite_list[Z_ORDER_MAX];
static Uint32 timeleft();
static int fade_out_in;
static gboolean flamme_draw;
static SDL_Surface *buffer_fade;
static unsigned int *flamme_color;
static unsigned char *flamme_scr1;
static unsigned char *flamme_scr2;

void SDL_init()
{
  if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) {
    g_printerr("Unable to init SDL: %s\n", SDL_GetError());
    exit(1);
  };
  atexit(SDL_Quit);
  screen = SDL_SetVideoMode(DISPLAY_W, DISPLAY_H, DISPLAY_BPP, SDL_SWSURFACE | SDL_DOUBLEBUF);
  if (!screen) {
    g_printerr("Unable to set %dx%d video: %s\n", DISPLAY_W, DISPLAY_H, SDL_GetError());
    exit(1);
  };
  transparent = SDL_MapRGB(screen->format, 240, 0, 240);
}

void loopwait()
{
  SDL_Delay(timeleft());
}

static Uint32 timeleft()
{
  enum {TICK_INTERVAL = 30};
  static Uint32 next_time = 0;
  Uint32 now;
  now = SDL_GetTicks();
  if (next_time <= now) {
    next_time = now + TICK_INTERVAL;
    return(0);
  };
  return(next_time - now);
}

void video_sprite_init()
{
  int i;
  for (i = 0; i < Z_ORDER_MAX; i++) {
    sprite_list[i] = NULL;
  };
  buffer_fade = SDL_CreateRGBSurface(SDL_SWSURFACE, screen->w, screen->h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
}

void video_sprite_free()
{
  int i;
  for (i = 0; i < Z_ORDER_MAX; i++) {
    g_list_foreach(sprite_list[i], (GFunc)my_free, NULL);
    g_list_free(sprite_list[i]);
  };
  SDL_FreeSurface(buffer_fade);
}

struct _sprite *video_sprite_add(int z_order)
{
  struct _sprite *sprite;
  g_assert((z_order < Z_ORDER_MAX) && (z_order >= 0));
  sprite = my_new(struct _sprite, 1);
  sprite->x = 0;
  sprite->y = 0;
  sprite->abs = FALSE;
  sprite->alive = TRUE;
  sprite->surface = NULL;
  sprite_list[z_order] = g_list_append(sprite_list[z_order], sprite);
  return(sprite);
}

SDL_Surface *video_bmp_load(char *file)
{
  SDL_Surface *temp, *surface;
  temp = SDL_LoadBMP(file);
  if (!(temp)) {
    g_printerr("Couldn't load %s: %s\n", file, SDL_GetError());
    exit(1);
  };
  surface = SDL_DisplayFormat(temp);
  if (!(surface)) {
    g_printerr("Couldn't DisplayFormat %s: %s\n", file, SDL_GetError());
    exit(1);
  };
  SDL_FreeSurface(temp);
  return surface;
}

SDL_Surface* video_crop(SDL_Surface *s, SDL_Rect *r)
{
  SDL_Surface *temp, *surface;
  temp = SDL_CreateRGBSurface(SDL_SWSURFACE, r->w, r->h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
  if (!(temp)) {
    g_printerr("failed CreateRGBSurface : %s\n", SDL_GetError());
    exit(1);
  };
  SDL_BlitSurface(s, r, temp, NULL);
  SDL_SetColorKey(temp, SDL_SRCCOLORKEY | SDL_RLEACCEL, transparent);
  surface = SDL_DisplayFormat(temp);
  SDL_FreeSurface(temp);
  if (!(surface)) {
    g_printerr("Couldn't DisplayFormat : %s\n", SDL_GetError());
    exit(1);
  };
  return surface;
}

SDL_Surface* video_crop_alpha(SDL_Surface *s, SDL_Rect *r, int alpha)
{
  SDL_Surface *temp, *surface;
  temp = SDL_CreateRGBSurface(SDL_SWSURFACE, r->w, r->h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
  if (!(temp)) {
    g_printerr("failed CreateRGBSurface : %s\n", SDL_GetError());
    exit(1);
  }; 
  SDL_SetColorKey(temp, SDL_SRCCOLORKEY | SDL_RLEACCEL, transparent);
  SDL_SetAlpha(temp, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
  SDL_BlitSurface(s, r, temp, NULL);
  surface = SDL_DisplayFormat(temp);
  SDL_FreeSurface(temp);
  if (!(surface)) {
    g_printerr("Couldn't DisplayFormat : %s\n", SDL_GetError());
    exit(1);
  };
  return surface;
}

void video_display()
{
  int i;
  GList *li, *next;
  struct _sprite *sprite;
  int x, y;
  SDL_Rect r;
  static int flamme_limiter = 0;
  if (flamme_draw) {
    if (flamme_limiter % 4 == 0) {
      video_flamme_draw();
      flamme_limiter = 0;
    };
    flamme_limiter++;
  } else {
    SDL_FillRect(screen, NULL,0x000000);
  };
  for (i = 0; i < Z_ORDER_MAX; i++) {
    for (li = g_list_first(sprite_list[i]); li != NULL; li = next) {
      next = g_list_next(li);
      sprite = (struct _sprite *)li->data;
      if (sprite->alive == FALSE) {
	sprite_list[i] = g_list_remove(sprite_list[i], sprite);
	my_free(sprite);
	continue;
      };
      if (sprite->surface == NULL) continue;
      if (sprite->abs == FALSE) {
	x = sprite->x - Player.sprite->x + (DISPLAY_W / 2);
	y = sprite->y - Player.sprite->y + (DISPLAY_H / 2);
      } else {
	x = sprite->x;
	y = sprite->y;
      };
      r.x = x;
      r.y = y;
      r.w = sprite->surface->w;
      r.h = sprite->surface->h;
      if (fade_out_in > 0) { 
	SDL_BlitSurface(sprite->surface, NULL, buffer_fade, &r);
      } else {
	SDL_BlitSurface(sprite->surface, NULL, screen, &r);
      };
    };
  };
  if (fade_out_in > 0) { 
    fade_out_in--;
    SDL_SetAlpha(buffer_fade, SDL_SRCALPHA, CLAMP(256 - (fade_out_in * 16), 0, 255));
    SDL_BlitSurface(buffer_fade, NULL, screen, NULL);
  };
  SDL_Flip(screen);
}

void video_fade()
{
  SDL_BlitSurface(screen, NULL, buffer_fade, NULL);
  for (fade_out_in = 32; fade_out_in > 16; fade_out_in--) {
    SDL_FillRect(screen, NULL,0x000000);
    SDL_SetAlpha(buffer_fade, SDL_SRCALPHA, CLAMP(-256 + (fade_out_in * 16), 0, 255));
    SDL_BlitSurface(buffer_fade, NULL, screen, NULL);
    SDL_Flip(screen);
    loopwait();
  };
}

void video_flamme_init()
{
  int i;
  int r,g,b;
  flamme_color = (int *) calloc ( 256 , 4 );
  for (i = 0; i < 256; i++ ) {  
    r = g = b = 0 ;
      
    if ( (i > 7) && (i < 32) )
      r = 10 * ( i - 7 );
    if ( i > 31 )
      r = 255;
      
    if ( (i > 32 ) && (i < 57 ) )
      g = 10 * ( i - 32 );
    if ( i > 56 )
      g = 255;
      
    if ( i < 8 )
      b = 8 * i;
    if ( (i > 7) && (i < 17) )
      b = 8 * ( 16 - i );
    if ( (i > 57) && (i < 82) )
      b = 10 * ( i - 57 );
    if ( i > 81 )
      b = 255;
    flamme_color[i] = (Uint16)SDL_MapRGB(screen->format, r, g, b);
  };
  flamme_scr1 = (unsigned char *) calloc ( DISPLAY_W*(DISPLAY_H+4) , 1 );
  flamme_scr2 = (unsigned char *) calloc ( DISPLAY_W*(DISPLAY_H+4) , 1 );
}

void video_flamme(gboolean flag)
{
  flamme_draw = flag;
}

void video_flamme_draw()
{
  int x, y;
  unsigned char *scr_tmp = flamme_scr1;
  int bcl,tmp;
  unsigned char *src;
  unsigned char *dst;
  unsigned char *swp;
  if ( SDL_LockSurface(screen) == 0 ) {
    Uint16 *row;
    for ( y=0; y<DISPLAY_H; ++y ) {
      row = (Uint16 *)((Uint8 *)screen->pixels + y*screen->pitch);
      for ( x=0; x<DISPLAY_W; ++x ) {
	*row++ = flamme_color [*(scr_tmp++)];
      }
    }
  }
  SDL_UnlockSurface(screen);

  for ( bcl=0 ; bcl<3*DISPLAY_W ; bcl++ )
    *(flamme_scr2+DISPLAY_W*DISPLAY_H+bcl) = 56 + g_random_int_range(0, 40);
  
  tmp = 30 + g_random_int_range(0, 40);
  for ( bcl=0 ; bcl<tmp ; bcl++ )
    {
      dst = flamme_scr2 + DISPLAY_W*(DISPLAY_H+1) + g_random_int_range(0, DISPLAY_W-3);

      *dst =
	*(dst+1) =
	*(dst+2) =
	*(dst+DISPLAY_W) =
	*(dst+DISPLAY_W+1) =
	*(dst+DISPLAY_W+2) =
	*(dst+2*DISPLAY_W+1) =
	*(dst+2*DISPLAY_W+2) =
	*(dst+2*DISPLAY_W+3) = 149;
      }

  src = flamme_scr2 + 2*DISPLAY_W;
  dst = flamme_scr1 + DISPLAY_W;

  for ( bcl=0 ; bcl<DISPLAY_W*(DISPLAY_H+2)-2 ; bcl++ )
    {

      tmp =   *(src+DISPLAY_W)
            + *(src+2*DISPLAY_W-1)
	    + *(src+2*DISPLAY_W)
	    + *(src+2*DISPLAY_W+1);
     
      tmp >>= 2;
      
      if (tmp != 0)
	*dst++ = tmp-1;
      else
	*dst++ = 0;

      src++;
	
    }

  swp = flamme_scr1;
  flamme_scr1 = flamme_scr2;
  flamme_scr2 = swp;

}

void video_flamme_free()
{
  free(flamme_color);
  free(flamme_scr1);
  free(flamme_scr2);
}
